/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * atom.c
 */

#include <stdlib.h>
#include <memory.h>
#include <limits.h>
#include <stdio.h>
#include "../log/logger.h"
#include "../basic/operator.h"

// (lex "Integer" "123")
Cell* op_lex(Scheme *sc) {
    LOG_INPUT(sc, "atoms/atom", "op_lex");

    Cell* type = first(sc->args);
    Cell* value = second(sc->args);
    TYPE_CHECK(sc, type, STRING_TYPE);
    TYPE_CHECK(sc, value, STRING_TYPE);
    Cell* result;

    if (strcmp(type->_string, BOOLEAN_TYPE) == 0) {
        if (strcmp(value->_string, "#t") == 0) {
            result = G_TRUE;
        } else if (strcmp(value->_string, "#f") == 0) {
            result = G_FALSE;
        } else {
            result = make_default_exception(sc, "NOT_A_BOOLEAN", "You should input #t or #f.");
        }
    } else if (strcmp(type->_string, CHARACTER_TYPE) == 0) {
        if (strcmp(value->_string, "#\\space") == 0) {
            result = make_character(sc, ' ');
        } else if (strcmp(value->_string, "#\\tab") == 0) {
            result = make_character(sc, '\t');
        } else if (strcmp(value->_string, "#\\return") == 0) {
            result = make_character(sc, '\r');
        } else if (strcmp(value->_string, "#\\newline") == 0) {
            result = make_character(sc, '\n');
        } else {
            int c = 0;
            char *name = value->_string;
            if (name[0] == '#' && name[1] == '\\' && sscanf(name + 2, "%c", (unsigned int *) &c) == 1 && c < UCHAR_MAX && !(name[3])) {
                result = make_character(sc, c);
            } else {
                result = make_default_exception(sc, "NOT_A_CHARACTER", "Not a correct character.");
            }
        }
    } else if (strcmp(type->_string, STRING_TYPE) == 0) {
        result = make_string(sc, value->_string);
    } else if (strcmp(type->_string, INTEGER_TYPE) == 0) {
        result = make_integer(sc, atol(value->_string));
    } else if (strcmp(type->_string, DECIMAL_TYPE) == 0) {
        result = make_decimal(sc, atof(value->_string));
    } else if (strcmp(type->_string, SYMBOL_TYPE) == 0) {
        result = make_symbol(sc, value->_string);
    } else {
        result = make_default_exception(sc, "UNDEFINED_LEX", "Lex rule undefined.");
    }

    sc->returnValue = result;

    LOG_OUTPUT(sc, "atoms/atom", "op_lex");
    return pop_callstack(sc);
}

void init_atom_operators(Scheme *sc) {
    ADD_OPERATOR(sc, "lex", lex);
}
